

#include "netlist.h"

int main(int argc, char ** argv)
{
	// File to be opened. Right now it's hard coded for debugging.
	char in_filename[25];
	char out_filename[25];

	strcpy(out_filename, "output.bench");
	switch(argc){
	case 1:
		printf("Give me at least an input file as parameter. Try again with:\n");
		printf("assignment2 inputfile.ext outputfile.ext\n");
		return 1;
		break;
	case 3:
		strcpy(out_filename, argv[2]);
		// Although there is a warning,
		//the statement is intended like this. We only change the output
		//name if we received an parameter for that.
	case 2:
		strcpy(in_filename, argv[1]);
		break;
	}


	FILE *myfile = fopen(in_filename, "r");
	FILE *outfile = fopen(out_filename, "w");
	// The circuit interface specifications (outputs and inputs).
	s_interface *myinterface = malloc(sizeof(s_interface));
	// The hash table containing all the elements.
	node **table = create_hash_table(100);
	// Read the netlist.
	read_netlist(myfile, myinterface, table, 100);

	write_netlist(outfile, myinterface, table, 100);

	clean_hash(table, 100);
	free(myinterface->inputs);
	free(myinterface->outputs);
	free(myinterface);
	free(table);
	printf("New netlist generated: %s\n", out_filename);
	printf("Press <Return> to exit...");
	getchar();
	return 0;
}

void to_lower_case(char *line)
{
	int i;
	for(i=0;line[i];i++)
	{
		line[i] = tolower(line[i]);
	}
}

void read_line(FILE *fp, char line[])
{
	char *tmp = fgets(line, LINE_BUF_SIZE, fp);
	if(!tmp){
		line[0]='\0';
	}
}

int isInput(char *line)
{
	int out = 0;
	char tmp_line[LINE_BUF_SIZE];
	strcpy(tmp_line,line);
	/*convert line to lower case which makes comparison easier*/
	to_lower_case(tmp_line);
	char *tmp = NULL;
	tmp = strstr(tmp_line, "input");
	if(tmp_line[0]=='#' || tmp_line[0]=='\n') //check if the line is a comment or just an empty line
	{
		out = 0;
	}
	else if(tmp != NULL) //compare strings, when the string contains "input" a pointer is returned
	{
		out = 1;
	}
	else
	{
		out = 0;
	}
	return out;
}

int isOutput(char *line)
{
	int out = 0;
	char tmp_line[LINE_BUF_SIZE];
	strcpy(tmp_line,line);

	/*convert line to lower case which makes comparison easier*/
	to_lower_case(tmp_line);

	if(tmp_line[0]=='#' || tmp_line[0]=='\n') //check if the line is a comment
	{
		out = 0;
	}
	else if(strstr(tmp_line, "output") != NULL) //compare strings, when the string contains "output" a pointer is returned
	{
		out = 1;
	}
	else
	{
		out = 0;
	}
	return out;
}

int isGate(char *line)
{
	int out = 0;
	char tmp_line[LINE_BUF_SIZE];
	strcpy(tmp_line,line);
	/*convert line to lower case which makes comparison easier*/
	to_lower_case(tmp_line);

	if(tmp_line[0]=='#' || tmp_line[0]=='\n') //check if the line is a comment
	{
		out = 0;
	}
	//If it's not a comment, neither input neither output, assume it's a gate.
	// Other method is to check for valid gate names.
	else if(strstr("input",tmp_line) == NULL && strstr("output",tmp_line) == NULL) //compare strings, when the string contains "input" or "output" a pointer is returned
	{
		out = 1;
	}
	else
	{
		out = 0;
	}
	return out;
}

void netlist_statistics(FILE *fp, int *no_inputs, int *no_outputs)
{
	char line[LINE_BUF_SIZE];


	/*read the whole file and count the number of inputs and outputs */
	read_line(fp,line);
	while(strlen(line))
	{
		if(isInput(line))
		{
			(*no_inputs)++;
		}
		else if(isOutput(line))
		{
			(*no_outputs)++;
		}
		read_line(fp,line);
	}

}

gate_type determine_gate_type(char *str)
{
	gate_type out;

	to_lower_case(str);
	if (isGate(str))
	{
		if(strstr(str, "nand") != NULL)
		{
			out = Nand;
		}
		else if(strstr(str,"and") != NULL)
		{
			out = And;
		}
		else if(strstr(str,"xnor") != NULL)
		{
			out = Xnor;
		}
		else if(strstr(str,"xor") != NULL)
		{
			out = Xor;
		}
		else if(strstr(str,"nor") != NULL)
		{
			out = Nor;
		}
		else if(strstr(str,"or") != NULL)
		{
			out = Or;
		}
		else if(strstr(str,"not") != NULL)
		{
			out = Not;
		}
	}

	return out;
}

circuit_item *get_or_insert_wire_item(node *table[], int table_size, char *str){

	//Get item from hashtable.
	circuit_item *tmp_circuit_item = get_circuit_item(table, table_size, str);

	// If nothing was returned, create a new wire.
	if(!tmp_circuit_item){
		tmp_circuit_item = malloc(sizeof(circuit_item));
		strcpy(tmp_circuit_item->name, str);
		tmp_circuit_item->type = Wire;
		// Create also the wire data
		s_wire tmp_wire;
		tmp_wire.in = NULL;
		tmp_wire.no_outputs = 0;
		tmp_wire.out = NULL;
		tmp_circuit_item->cir_type.circuit_wire = tmp_wire;
		insert(table, table_size, str, tmp_circuit_item);
	}

	return tmp_circuit_item;
}

void input_statement(char *line, circuit_item *inputs[], int *wr_index, node *table[], int table_size){

	int i = 0;
	int ended = 0;
	char name[20];
	// Compare the begin.
	if(isInput(line)){
		while(!ended){
			// The offset is to read only after "input("
			char tmp = line[6+i];
			switch(tmp){
			case '\0':
				printf("Closing bracket not found in input statement:\n%s\n", line);
				ended = -1;
				break;
			case ')':
				if(i==0){
					printf("Empty input name in line:\n%s\n", line);
				}else{
					ended = 1;
				}
				name[i]='\0';
				break;
			default:
				name[i] = tmp;
				i++;
				break;
			}
		}
	}else{
		printf("This line is not an input\n");
	}
	if(ended==1){
		inputs[(*wr_index)] = get_or_insert_wire_item(table, table_size, name);
		(*wr_index)++;
	}
}

void output_statement(char *line, circuit_item *outputs[], int *wr_index, node *table[], int table_size	)
{
	int i = 0;
	int ended = 0;
	char name[20];
	if(isOutput(line)){
		while(!ended){
			// The offset is to read only after "input("
			char tmp = line[7+i];
			switch(tmp){
			case '\0':
				printf("Closing bracket not found in output statement:\n%s\n", line);
				ended = -1;
				break;
			case ')':
				if(i==0){
					printf("Empty output name in line:\n%s\n", line);
				}else{
					ended = 1;
					name[i]='\0';
				}
				break;
			default:
				name[i] = tmp;
				i++;
				break;
			}
		}
	}else{
		printf("This line is not an output\n");
	}
	if(ended==1){
		outputs[(*wr_index)] = get_or_insert_wire_item(table, table_size, name);
		(*wr_index)++;
	}
}


void gate_statement(char *line, node *table[], int table_size){

	circuit_item *out_wire;
	circuit_item *gate;
	gate_type my_gate_tp;
	int n_inputs;
	// The 3 parts we need out of a string.
	char gate_type_str[NAME_BUF_SIZE];
	char output_name[NAME_BUF_SIZE];
	char input_list[LINE_BUF_SIZE];

	// Control variables in my loop.
	int ended=0;
	int i = 0; // Char index on line
	int j = 0;

	// Check the first Label (circuit output).
	while(!ended){
		char tmp = line[i];

		switch(tmp){
		// Different chars we don't eto_lowerxpect in a gate name.
		case '(':
		case ')':
		case ':':
		case ',':
		case '\0':
		case '\n':
		case '#':
			printf("Invalid output name in gate statement:\n%s\n", line);
			ended = -1;
			break;
		case ' ':
			i++;
			break;
		case '=':
			output_name[j] = '\0';
			if(strlen(output_name)){
				ended = 1;
			}else{
				ended = -2;
			}
			i++;
			break;
		default:
			output_name[j] = tmp;
			i++;
			j++;
			break;
		}
	}
	if(ended < 0){
		return;
	}
	ended = 0;
	j = 0;
	while(!ended){
		char tmp = line[i];

		switch(tmp){
		// Different chars we don't expect in a gate name.
		case '=':
		case ')':
		case ':':
		case ',':
		case '\0':
		case '\n':
			printf("Invalid gate type in gate statement:\n%s\n", line);
			ended = -1;
			break;
		case ' ':
			i++;
			break;
		case '(':
			gate_type_str[j]='\0';
			if(strlen(gate_type_str)){
				ended = 1;
			}else{
				ended = -2;
			}
			i++;
			break;
		default:
			gate_type_str[j] = tmp;
			i++;
			j++;
			break;
		}
	}
	if(ended < 0){
		return;
	}
	ended = 0;
	j = 0;
	while(!ended){
		char tmp = line[i];

		switch(tmp){
		// Different chars we don't expect in a gate name.
		case '(':
		case '#':
		case '=':
		case ':':
		case '\0':
		case '\n':
		case '{':
		case '}':
			printf("Invalid gate type in gate statement:\n%s\n", line);
			ended = -1;
			break;
		case ' ':
			i++;
			break;
		case ')':
			input_list[j]='\0';
			if(strlen(input_list)){
				ended = 1;
			}else{
				ended = -2;
			}
			i++;
			break;
		default:
			input_list[j] = tmp;
			i++;
			j++;
			break;
		}
	}
	if(ended < 0){
		return;
	}

	my_gate_tp = determine_gate_type(gate_type_str);
	n_inputs = count_occurences(input_list, ',') + 1;

	out_wire = get_or_insert_wire_item(table, table_size, output_name);
	gate = malloc(sizeof(circuit_item));
	GET_WIRE(out_wire).in = gate;
	//create the name of the gate: gate_type + output_name
	strcat(gate_type_str, output_name);
	strcpy(gate->name, gate_type_str);
	gate->type = Gate;
	insert(table, table_size, gate->name, gate);
	s_gate my_gate;
	my_gate.type = my_gate_tp;
	my_gate.no_inputs = n_inputs;
	my_gate.in = malloc(sizeof(circuit_item*)*n_inputs);
	my_gate.out = out_wire;

	GET_GATE(gate) = my_gate;
	//create all inputs by splitting on , and inserting the inputs.
	char *input_name = strtok(input_list, ",");
	j = 0;
	for(j=0; j < n_inputs; j++){
		circuit_item *tmp = get_or_insert_wire_item(table, table_size, input_name);
		s_wire input_wire = GET_WIRE(tmp);
		input_wire.no_outputs++;

		input_wire.out = realloc(input_wire.out, sizeof(circuit_item*)*input_wire.no_outputs);
		input_wire.out[input_wire.no_outputs-1] = gate;
		GET_GATE(gate).in[j] = tmp;
		GET_WIRE(tmp) = input_wire;
		input_name = strtok(NULL, ","); //goto next label
	}

}

int count_occurences(char *line, char id){
	int count=0;
	int i=0;
	while(line[i]){
		if(line[i]==id){
			count++;
		}
		i++;
	}

	return count;
}

void read_netlist(FILE *fp, s_interface *interface, node *table[], int table_size){
	int noinputs=0;
	int nooutputs=0;
	char line[LINE_BUF_SIZE];


	//first count number of inputs and outputs to determine the needed memory allocation
	netlist_statistics(fp, &noinputs, &nooutputs);

	int in_index = 0;
	int out_index = 0;
	interface->no_inputs = noinputs;
	interface->no_outputs = nooutputs;

	interface->inputs = malloc(sizeof(circuit_item*)*noinputs);
	interface->outputs = malloc(sizeof(circuit_item*)*nooutputs);
	rewind(fp);

	read_line(fp, line);
	while(strlen(line))
	{
		if(isInput(line))
		{
			input_statement(line, interface->inputs,&in_index, table, table_size);
		}
		else if(isOutput(line))
		{
			output_statement(line, interface->outputs,&out_index, table, table_size);
		}
		else if(isGate(line))
		{
			gate_statement(line,table,table_size);
		}
		else
		{
			//printf("read_netlist: error in reading the file, not defined\n%s\n", line);
		}
		read_line(fp, line);
	}
}

void write_netlist(FILE *fp_out, s_interface *interface, node *table[], int table_size){
	int i=0;
	for(i=0; i < interface->no_inputs; i++){
		fprintf(fp_out, "input(%s)\n", interface->inputs[i]->name);
	}
	fprintf(fp_out,"\n");
	for(i=0; i < interface->no_outputs; i++){
		fprintf(fp_out, "output(%s)\n", interface->outputs[i]->name);
	}
	fprintf(fp_out,"\n");
	// Loop throught all elements of the table.
	for(i=0; i < table_size; i++){

		node *tmp = table[i];
		// Loop through the items at table location [i]
		while(tmp != NULL){
			char line[LINE_BUF_SIZE];
			// If it's a gate, generate the line, and print it.
			if(tmp->net_item->type==Gate){
				s_gate tmp_gate = GET_GATE(tmp->net_item);
				if(generate_gate_line(tmp_gate, line)){
					fprintf(fp_out, "%s\n", line);
				}
			}
			tmp = tmp->next;
		}
	}
}

int generate_gate_line(s_gate gate, char *line){

	gate_type gtp = gate.type;
	// Add initially the output wire name.
	strcpy(line, gate.out->name);
	strcat(line, " = ");
	switch(gtp){
	case Not:
		strcat(line, "not");
		break;
	case And:
		strcat(line, "and");
		break;
	case Nand:
		strcat(line, "nand");
		break;
	case Or:
		strcat(line, "or");
		break;
	case Nor:
		strcat(line, "nor");
		break;
	case Xor:
		strcat(line, "xor");
		break;
	case Xnor:
		strcat(line, "xnor");
		break;
	default:
		return 0;
		break;

	}
	strcat(line, "(");
	int i = 0;
	for(i=0; i < gate.no_inputs; i++){
		strcat(line, gate.in[i]->name);
		if(i!=gate.no_inputs-1){
			strcat(line, ",");
		}
	}
	strcat(line, ")");
	return strlen(line);
}

void free_circuititem(circuit_item *item){
	if(item->type==Gate){
		free(item->cir_type.circuit_gate.in);
	}else{
		free(GET_WIRE(item).out);
	}
	free(item);
}
